/**********************************************************************************
// Asteroids
//
// Creation:	14 Set 2008
// Updated:		02 out 2008
// Compiler:	Visual Studio 2008
//
// Notes:		Test the Anabele engine
//				Remake of classical asteroids game
//
**********************************************************************************/

#define WIN32_LEAN_AND_MEAN  // just say no to MFC

#include "Resources.h"
#include "Anabele.h"	
#include "SpaceShip.h"
#include "Rock.h"
#include "Missile.h"

#include <algorithm>
#include <iterator>
#include <list>
using std::list;

/**********************************************************************************/
/*                            Asteroids Class                                     */
/**********************************************************************************/

enum KeysAndSound {UP, DOWN, MISSILE, EXPLOSION};

enum State {HOME, LEVEL1, LEVEL2, LEVEL3, LEVEL4, LEVEL5, GAMEOVER};


class Asteroids: public Game
{
private:
	SceneManager scene;
	Sound snd;

	int state;

	bool keyDownUp[3];
	char buffer[120];

	SpaceShip plane;
	Rect      border[4];
	
	list<Rock *> rocks;
	list<Rock *>::iterator ind;

	list<Missile *> missiles;
	list<Missile *>::iterator pos;

public:
	void GameInit();
	void GameLogic();
	void GameDraw();
	void GameShutdown();
};

/**********************************************************************************/

void Asteroids::GameInit()
{
	// game starts at home screen
	state = HOME;

	// Create the Plane
	Point planeVertex[4] = {Point(0,-10), Point(7,10), Point(0,7), Point(-7,10)};
	plane.Vertices(planeVertex, 4);
	plane.MoveTo(GetWindowWidth()/2, GetWindowHeight()/2);
	scene.RegisterMoving(plane);

	// Create Borders:
	// the borders are positioned around the screen
	// they ensure every missile will collide with something
	// and than no need to test "out of screen" for missiles
	border[0] = Rect(0, 0, GetWindowWidth()-1, 50);
	border[0].MoveTo(0, -50);
	scene.RegisterStatic(border[0]);

	border[1] = Rect(0, 0, GetWindowWidth()-1, 50);
	border[1].MoveTo(0, GetWindowHeight()-1);
	scene.RegisterStatic(border[1]);

	border[2] = Rect(0, 0, 50, GetWindowHeight()-1);
	border[2].MoveTo(-50, 0);
	scene.RegisterStatic(border[2]);

	border[3] = Rect(0, 0, 50, GetWindowHeight()-1);
	border[3].MoveTo(GetWindowWidth()-1, 0);
	scene.RegisterStatic(border[3]);
	
	// initialize and load sound effects
	snd.Initialize(*this);	
	snd.Load("JetWash.wav");
	snd.Frequency(UP,44100);
	
	snd.Load("JetWash.wav");
	snd.Frequency(DOWN,22050);
	snd.Volume(DOWN, 9500);

	snd.Load("Missile.wav");
	snd.Frequency(MISSILE,44100);
	snd.Volume(MISSILE, 9200);

	snd.Load("Explosion.wav");
	snd.Frequency(EXPLOSION,11025);
	snd.Volume(EXPLOSION, 9000);

	// initialize up-down key states
	keyDownUp[UP]      = true;
	keyDownUp[DOWN]    = true;
	keyDownUp[MISSILE] = true;
}

/**********************************************************************************/

void Asteroids::GameLogic()
{
	// Quits by pressing ESC
	EndsOnEscape();

	if (state != GAMEOVER)
	{	
		// Rotate
		if (KeyDown(VK_RIGHT)) // rotate right
			plane.Rotate(4);
		else
		if (KeyDown(VK_LEFT)) // rotate left
			plane.Rotate(-4);

		// Detect key release
		if (KeyUp(VK_UP))    
		{
			keyDownUp[UP] = true;
			snd.Stop(0);
		}
		if (KeyUp(VK_DOWN))    
		{
			keyDownUp[DOWN] = true;
			snd.Stop(1);
		} 
		if (KeyUp(VK_SPACE))    
		{
			keyDownUp[MISSILE] = true;
		} 

		// Translate (move up and down)
		if (KeyDown(VK_UP))    
		{
			// translate up
			plane.AccelerateUp();
			if (keyDownUp[UP])
			{
				snd.Play(UP,0);
				keyDownUp[UP] = false;
			}		
		}
		else
		if (KeyDown(VK_DOWN))  
		{
			// translate down
			plane.AccelerateDown();
			if (keyDownUp[DOWN])
			{
				snd.Play(DOWN,0);
				keyDownUp[DOWN] = false;
			}
		}

		// Space Bar (Fire Missile)
		if (KeyDown(VK_SPACE))
		{
			if (keyDownUp[MISSILE])
			{
				snd.Play(MISSILE,0);
				missiles.push_back(new Missile(plane));
				scene.RegisterMoving(*(missiles.back()));
				keyDownUp[MISSILE] = false;
			}
		}

		// move plane by its inertia
		plane.Inertia();

		// move all the missiles
		for (pos = missiles.begin(); pos != missiles.end(); ++pos)
			(*pos)->Move();

		// move all the rocks
		for (ind = rocks.begin(); ind != rocks.end(); ++ind)
			(*ind)->Move();

		// test for collision
		if (scene.CollisionDetection())
		{
			pair<GameObject*,GameObject*> pairObjs;

			while ((pairObjs = scene.OnCollisionObjects()) != pair<GameObject*,GameObject*>(NULL,NULL))
			{
				Geometry * first  = (Geometry *) pairObjs.first;
				Geometry * second = (Geometry *) pairObjs.second;

				// Missile
				if (first->GetType() == ObjPoint)
				{
					snd.Play(EXPLOSION,0);
					// unregister missile from scene manager
					scene.UnRegisterMoving(*first);
					// remove missile from the list
					missiles.remove((Missile*) first);
					// delete missile object
					delete (Missile*) first;
				}
				// Missile
				if (second->GetType() == ObjPoint)
				{
					snd.Play(EXPLOSION,0);
					// unregister missile from scene manager
					scene.UnRegisterMoving(*second);
					// remove missile from the list
					missiles.remove((Missile*) second);
					// delete missile object
					delete (Missile*) first;
				}
				// Rock and Missile
				if (first->GetType() == ObjPolygon && second->GetType() == ObjPoint)
				{
					snd.Play(EXPLOSION,0);
					// unregister rock from scene manager
					scene.UnRegisterMoving(*first);
					// remove rock from the list
					rocks.remove((Rock*) first);
					// delete rock object
					delete (Rock*) first;
				}
				// Plane and Border
				if (first->GetType() == ObjPolygon && second->GetType() == ObjRectangle)
				{
					snd.Play(EXPLOSION,0);
					// unregister plane from scene manager
					scene.UnRegisterMoving(*first);
					state = GAMEOVER;
				}
				
			
			} // end while
		} // end test for collision
	} // game over
} 

/**********************************************************************************/

void Asteroids::GameDraw()
{
	if (state == GAMEOVER)
	{
		// Draw Text
		sprintf_s(buffer, "Asteroids Race");
		Text(buffer, GetWindowWidth()/2-50, 10, RGB(255,255,255));

		sprintf_s(buffer, "Game Over");
		Text(buffer, GetWindowWidth()/2-25, GetWindowHeight()/2, RGB(255,255,255));
	}
	else
	{
		// Lock screen for pixel plotting functions
		LockScreen();

		// Draw polygons
		Draw(plane, plane.GetColor());
	
		Draw(border[0], border[0].GetColor());
		Draw(border[1], border[1].GetColor());
		Draw(border[2], border[2].GetColor());
		Draw(border[3], border[3].GetColor());

		for (ind = rocks.begin(); ind != rocks.end(); ++ind)
			Draw(**ind, (*ind)->GetColor());
	
		for (pos = missiles.begin(); pos != missiles.end(); ++pos)
			Draw(**pos, (*pos)->GetColor());
				
		// UnLock screen when pixel plotting is done
		UnLockScreen();
	
		// Draw Text
		sprintf_s(buffer, "Asteroids Race");
		Text(buffer, GetWindowWidth()/2-50, 10, RGB(255,255,255));

		sprintf_s(buffer, "Use the keys:    <Arrows> - Move space ship    <ESC> Exit");
		Text(buffer, GetWindowWidth()/2-200, GetWindowHeight()-30, RGB(255,255,255));
	}
} 

/**********************************************************************************/

void Asteroids::GameShutdown()
{
	// Destroy all existing missiles
	for (pos = missiles.begin(); pos != missiles.end(); ++pos)
		delete *pos;

	// Destroy all existing rocks
	for (ind = rocks.begin(); ind != rocks.end(); ++ind)
		delete *ind;
}

/**********************************************************************************/
/*                                  WinMain                                       */
/**********************************************************************************/

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
	Asteroids astGame;

	astGame.SetWindowBgColor(10,25,55);
	astGame.SetWindowTitle(string("Asteroids Race"));
	astGame.SetWindowIcon(LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON)));
	astGame.ShowWindowCursor(false);
	astGame.Initialize(hInstance);
	astGame.Start();
	
	return 0;

}

/**********************************************************************************/